﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using OutlookContactsSync.Domain.Globals;
using OutlookContactsSync.Domain.Model;
using OutlookContactsSync.Domain.Presenters;
using OutlookContactsSync.Domain.Properties;
using OutlookContactsSync.Domain.Repository;
using OutlookContactsSync.Domain.Views;
using OutlookContactsSync.Services;
using Settings = OutlookContactsSync.Domain.Properties.Settings;

namespace OutlookContactsSync.Presenters
{
    public class OutlookSyncMainPresenter : IOutlookSyncMainPresenter
    {
        private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker();
        private readonly IContactRepository _contactRepository;
        private readonly IOutlookRepository _outlookRepository;
        public bool SyncCancelled;
        private int _currentLowerProgressBoundary;
        private int _currentUpperProgressBoundary;
        private List<Contact> _domainUsers = new List<Contact>();
        private List<Contact> _outlookUsers = new List<Contact>();
        private string _progressMessage = string.Empty;
        private IOutlookSyncMainView _view;

        public OutlookSyncMainPresenter(IOutlookSyncMainView view,
                                        IContactRepository contactRepository,
                                        IOutlookRepository outlookRepository)
        {
            Guard.ArgumentNotNull(contactRepository, "contactRepository");
            Guard.ArgumentNotNull(outlookRepository, "outlookRepository");

            _contactRepository = contactRepository;
            _outlookRepository = outlookRepository;

            View = view;
        }

        #region IOutlookSyncMainPresenter Members

        public void InitView()
        {
            AddEventHandlers();
            View.ShowForm();
        }

        public IOutlookSyncMainView View
        {
            private get
            {
                Guard.ArgumentNotNull(_view, "view");
                return _view;
            }
            set { _view = value; }
        }

        #endregion

        private void AddEventHandlers()
        {
            View.StartSync += StartSync;
            View.CancelSync += CancelSync;

            _outlookRepository.ReportProgress += ReportProgressBack;

            // backgroundWorker
            _backgroundWorker.WorkerReportsProgress = true;
            _backgroundWorker.WorkerSupportsCancellation = true;
            _backgroundWorker.DoWork += BackgroundWorkerDoWork;
            _backgroundWorker.ProgressChanged += BackgroundWorkerProgressChanged;
            _backgroundWorker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted;
        }

        private void StartSync(object sender, EventArgs e)
        {
            Trace.WriteLine("Sync started", "info");
            SyncCancelled = false;

            _backgroundWorker.RunWorkerAsync();
        }

        private void CancelSync(object sender, EventArgs e)
        {
            Trace.WriteLine("Sync cancelled", "info");
            SyncCancelled = true;

            if (_backgroundWorker.WorkerSupportsCancellation)
            {
                _backgroundWorker.CancelAsync();
            }
        }

        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                SyncContacts();
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("Sync failed. Exception message: {0} ", ex.Message), "Exception");

                View.ExceptionMessage = ex.Message;

                var progressObject = new ProgressDTO
                                         {
                                             Message = Resources.ExceptionOccured,
                                             ShowProgressCounter = false,
                                             ContactPicture = Resources.exception
                                         };

                _backgroundWorker.ReportProgress(100, progressObject);
            }
        }

        private void SyncContacts()
        {
            var sw = new Stopwatch();
            sw.Start();

            //Get domain users
            InitCurrentProgressVariables(1, 20, Resources.GetDomainUsers);
            Trace.WriteLine("Get domain users", "info");

            if (!SyncCancelled) _domainUsers = _contactRepository.GetDomainUsers(ref SyncCancelled);

            //Get users from outlook
            InitCurrentProgressVariables(20, 40, Resources.GetOutlookUsers);
            Trace.WriteLine("Get users from outlook", "info");

            if (!SyncCancelled)
                _outlookUsers = _outlookRepository.GetOutlookUsers(ref SyncCancelled);

            //Update outlook users
            InitCurrentProgressVariables(40, 60, Resources.UpdateOutlookUsers);
            Trace.WriteLine("Update outlook users", "info");

            int updatedUsers = 0;
            if (!SyncCancelled)
            {
                updatedUsers = _outlookRepository.UpdateOutlookUsers(_outlookUsers, _domainUsers, ref SyncCancelled);
            }

            //Remove users old users from Outlook
            InitCurrentProgressVariables(60, 80, Resources.RemoveOutlookUsers);
            Trace.WriteLine("Remove users old users from Outlook", "info");

            int removedUsers = 0;
            if (!SyncCancelled)
            {
                removedUsers = _outlookRepository.RemoveOutlookUsers(_outlookUsers, _domainUsers, ref SyncCancelled);
            }

            //Add users to Outlook
            InitCurrentProgressVariables(80, 99, Resources.AddOutLookUsers);
            Trace.WriteLine("Remove users old users from Outlook", "info");

            int newUsers = 0;
            if (!SyncCancelled)
            {
                newUsers = _outlookRepository.AddOutLookUsers(_outlookUsers, _domainUsers, ref SyncCancelled);
            }

            //Sync completed, some administrative work
            var progressObject = new ProgressDTO
                                     {
                                         Message = Resources.Ready,
                                         ShowProgressCounter = false,
                                         ContactPicture = (SyncCancelled ? Resources.exception : Resources.succes)
                                     };
            _backgroundWorker.ReportProgress(100, progressObject);

            Trace.WriteLine("Sync completed, saving statistics", "info");

            //Save the different status variables, is shown in the status screen.
            Settings.Default.LastSyncTimeSpan = sw.Elapsed;
            sw.Reset();

            Settings.Default.LastSyncDateTime = DateTime.Now.ToString("dd-MM-yyyy");
            Settings.Default.LastSyncNewUsersAdded = newUsers;
            Settings.Default.LastSyncUsersUpdated = updatedUsers;
            Settings.Default.LastSyncUsersRemoved = removedUsers;
            Settings.Default.Save();

            Trace.WriteLine("Sync totally completed", "info");
        }

        private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var progressObject = (ProgressDTO) e.UserState;

            View.ProgressMessage = progressObject.Message;
            View.Progress = e.ProgressPercentage;
            View.ShowProgressCounter = progressObject.ShowProgressCounter;
            View.ProgressCounter = string.Format("{0} / {1}", progressObject.CurrentItem,
                                                 progressObject.CurrentAmountOfWorkItems);

            if (progressObject.ContactPicture != null)
            {
                View.ContactPicture = progressObject.ContactPicture;
            }
        }

        private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            View.Syncing = false;
        }

        private void ReportProgressBack(object sender, ProgressEventArgs e)
        {
            if (e.CurrentItem < 10 || e.CurrentItem%10 == 0) //Do not report back every cycle (report back up to 10 items, after then every 10 items)
            {
                Guard.ArgumentPositive(_currentLowerProgressBoundary, "Lower Progress boundary");
                Guard.ArgumentPositive(_currentUpperProgressBoundary, "Upper Progress boundary");
                Guard.StringNotNullOrEmpty(_progressMessage, "Progress Message");

                double progressPercentage = ((e.CurrentItem/e.CurrentAmountOfWorkItems) * 
                                             (_currentUpperProgressBoundary - _currentLowerProgressBoundary)) +_currentLowerProgressBoundary;

                var progressDto = new ProgressDTO
                                      {
                                          Message = _progressMessage,
                                          CurrentAmountOfWorkItems = e.CurrentAmountOfWorkItems.ToString(),
                                          CurrentItem = e.CurrentItem.ToString(),
                                          ShowProgressCounter = true
                                      };

                //Send current picture (if there is any) to view
                if (e.Picture != null)
                {
                    progressDto.ContactPicture = e.Picture;
                }

                _backgroundWorker.ReportProgress((int) progressPercentage, progressDto);
            }
        }

        private void InitCurrentProgressVariables(int lowerboundary, int upperboundary, string message)
        {
            _currentLowerProgressBoundary = lowerboundary;
            _currentUpperProgressBoundary = upperboundary;
            _progressMessage = message;
        }
    }
}